package com.acme.mock;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

import com.acme.cli.BankApplication;
import com.acme.domain.bank.Bank;
import com.acme.domain.bank.Client;
import com.acme.service.bank.BankDataLoader;
import com.acme.service.bank.BankService;
import com.acme.service.bank.impl.BankServiceImpl;

// TODO: [VY] Why this class at the mock package? Mocks are temp obj used fot testing ;)
public class BankServer {
	ServerSocket providerSocket = null;
	Socket connection = null;
	ObjectOutputStream out = null;
	ObjectInputStream in = null;
	String message = null;
	BankDataLoader loader = null;
	BankService bankService = null;

	BankServer() { }
	
	public static void startServer() {
		BankServer server = new BankServer();
		while(true) {
			server.run();
		}
	}

	// TODO: [VY] Method run make sence at the Runnable objects ;)
	// Your method is too long. Please split it for small peaces of code
	void run() {
		try {
			providerSocket = new ServerSocket(2004, 10);
			// wait for connection
			System.out.println("Waiting for connection...");
			connection = providerSocket.accept();
			System.out.println("Connection received from "
					+ connection.getInetAddress().getHostName());
			// get Input and Output streams
			out = new ObjectOutputStream(connection.getOutputStream());
			out.flush();
			in = new ObjectInputStream(connection.getInputStream());
			sendMessage("Connection successful");
			loader = new BankDataLoader();
			bankService = new BankServiceImpl();
			// the two parts communicate via the input and output streams
			do {
				try {
					message = (String) in.readObject();
					System.out.println("client> " + message);
					if(message.indexOf("add") > -1) {
						String report = this.addClients(message);
						sendMessage(report);
					}
					if(message.indexOf("getbalance") > -1) {
						String report = this.getAccountBalance(message);
						sendMessage(report);
					}
					if (message.equals("bye"))
						sendMessage("bye");
					
				} catch (ClassNotFoundException classnot) {
					System.err.println("Data received in unknown format");
				}
			} while (!message.equals("bye"));
		} catch (IOException ioException) {
			ioException.printStackTrace();
		} finally {
			// closing connection
			try {
				in.close();
				out.close();
				providerSocket.close();
			} catch (IOException ioException) {
				ioException.printStackTrace();
			}
		}
	}

	void sendMessage(final String msg) {
		try {
			out.writeObject(msg);
			out.flush();
			System.out.println("server>" + msg);
		} catch (IOException ioException) {
			ioException.printStackTrace();
		}
	}
	
	private String addClients(String message) throws IOException {
		String[] args = message.split(" ");
		// get new bank record as argument
		String newClientsFeedData = args[1];
		ArrayList<Client> newClients = loader.addClientsFromFeed(newClientsFeedData);
		String report = null;
		if(newClients != null) {
			report = BankReport.generateAddClientsToBankReport(newClients);
		}
		else {
			throw new IOException("The operation can't be completed!");
		}
		return report;
	}
	
	private String getAccountBalance(String message) throws IOException {
		String[] args = message.split(" ");
		int accountId = Integer.parseInt(args[1]);
		
		Bank bank = null;
		try {
			bank = BankApplication.loadBankFromDataSource();
		} catch (IOException e) {
			e.printStackTrace();
		}
		double balance = bankService.getAccountBalance(bank, accountId);
		String report = null;
		if(balance > -1) {
			report = BankReport.generateGetAccountBalanceReport(accountId, balance);
		}
		else {
			throw new IOException("The operation can't be completed!");
		}
		return report;
	}
}